/*
XPTracker - Alternative Agile Tracking Tool
Copyright (C) 2006-2009  Stephen Starkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package org.xptracker.domain;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.hibernate.annotations.Proxy;
import org.xptracker.persistence.Identified;

import javax.persistence.*;
import java.util.*;

@Entity
@Table(name="timelines")
@Proxy(lazy=false)
public class Timeline extends Identified {
  private Map<String, Criterion> entryCriteria = new HashMap<String, Criterion>();
  private Map<String, Criterion> exitCriteria = new HashMap<String, Criterion>();
  private Set<TimeSegment> segments = new LinkedHashSet<TimeSegment>();
  private String name;
  private TimelineGranularity granularity = new TimelineGranularity();
  private TimelineUnits units;

  public Timeline() {
  }

  public Timeline(String name, TimelineGranularity granularity, TimelineUnits units) {
    this.units = units;
    this.granularity = granularity;
    this.name = name;
  }

  @OneToMany(cascade=CascadeType.ALL)
  @JoinTable(name="timeline_time_segments_mapping")
  public Set<TimeSegment> getSegments() {
    return segments;
  }

  public void setSegments(Set<TimeSegment> segments) {
    this.segments = segments;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  @OneToMany(cascade=CascadeType.ALL)
  @JoinTable(name="timeline_entry_criteria_mapping")
  protected Map<String, Criterion> getEntryCriteriaInternal() {
    return entryCriteria;
  }

  protected void setEntryCriteriaInternal(Map<String, Criterion> entryCriteria) {
    this.entryCriteria = entryCriteria;
  }

  @Transient
  public Collection<Criterion> getEntryCriteria() {
    return getEntryCriteriaInternal().values();
  }

  @OneToMany(cascade=CascadeType.ALL)
  @JoinTable(name="timeline_exit_criteria_mapping")
  protected Map<String, Criterion> getExitCriteriaInternal() {
    return exitCriteria;
  }

  protected void setExitCriteriaInternal(Map<String, Criterion> exitCriteria) {
    this.exitCriteria = exitCriteria;
  }

  @Transient
  public Collection<Criterion> getExitCriteria() {
    return exitCriteria.values();
  }

  @OneToOne(cascade=CascadeType.ALL)
  public TimelineGranularity getGranularity() {
    return granularity;
  }

  public void setGranularity(TimelineGranularity granularity) {
    this.granularity = granularity;
  }

  public void setUnits(TimelineUnits units) {
    this.units = units;
  }

  public TimelineUnits getUnits() {
    return units;
  }

  public void removeCriterion(Criterion criterion) {
    if (entryCriteria.remove(criterion.getId()) == null) {
      exitCriteria.remove(criterion.getId());
    }
  }

  public boolean addEntryCriterion(Criterion criterion) {
    return addCriterion(entryCriteria, criterion);
  }

  public boolean addExitCriterion(Criterion criterion) {
    return addCriterion(exitCriteria, criterion);
  }

  private boolean addCriterion(Map<String, Criterion> criteria, Criterion criterion) {
    if (criteria.containsValue(criterion)) {
      return false;
    }
    criteria.put(criterion.getId(), criterion);
    return true;
  }

  @Transient
  public TimeSegment getCurrentSegment() {
    return (TimeSegment) CollectionUtils.find(segments, new Predicate() {
      public boolean evaluate(Object object) {
        TimeSegment seg = (TimeSegment) object;
        Date now = new Date();
        return seg.getStartDate().before(now) &&
            (seg.getStopDate().equals(now) || seg.getStopDate().after(now));
      }
    });
  }

  public void clearCriteriaForMigration() {
    entryCriteria = new HashMap<String, Criterion>();
    exitCriteria = new HashMap<String, Criterion>();
  }
}
